package com.mxx.common.redis;

import org.springframework.data.redis.core.ListOperations;

import java.util.Iterator;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * @author: mxx
 * @date: 2022/2/24
 * @time: 9:11
 * @Description: 描述信息
 */
public class BatchRedisList<T> implements Iterable<T> {

    private Integer batchNumber;
    private String cacheKey;
    private ListOperations<String, T> listOperations;


    public BatchRedisList(Integer batchNumber, String cacheKey, ListOperations<String, T> listOperations) {
        this.batchNumber = batchNumber;
        this.cacheKey = cacheKey;
        this.listOperations = listOperations;
    }

    public void eachAll(Consumer<T> consumer) {
        long total = listOperations.size(cacheKey);
        batch(total, (start, end) -> {
            List<T> list = listOperations.range(cacheKey, start, end - 1);
            for (T t : list) {
                consumer.accept(t);
            }
        });
    }

    public void eachAllByList(Consumer<List<T>> consumer) {
        long total = listOperations.size(cacheKey);
        batch(total, (start, end) -> {
            List<T> list = listOperations.range(cacheKey, start, end - 1);
            consumer.accept(list);

        });
    }

    public void pushAll(List<T> list) {
        long total = list.size();
        batch(total, (start, end) -> {
            listOperations.rightPushAll(cacheKey, list.subList(Long.valueOf(start).intValue(), Long.valueOf(end).intValue()));
        });
    }

    public void delete() {
        this.listOperations.getOperations().delete(cacheKey);
    }

    private void batch(Long total, BiConsumer<Long, Long> consumer) {
        if (total == 0) {
            return;
        }
        if (total < batchNumber) {
            consumer.accept(0L, total);
        }
        long i = 0, j = batchNumber;
        do {
            if (j <= total) {
                consumer.accept(i, j);
            }
            i = j;
            j += batchNumber;
            if (j > total) {
                j = total;
            }
        } while (i < total);
    }

    public int size() {
        return Long.valueOf(listOperations.size(cacheKey)).intValue();
    }

    private static class BatchRedisListIterator<T> implements Iterator<T> {
        private BatchRedisList<T> redisList;
        private Long total;
        private Integer currentIndex;
        private List<T> dataCache;
        private long end = 0;
        private long start = 0;

        public BatchRedisListIterator(BatchRedisList<T> redisList) {
            this.redisList = redisList;
            this.total = Integer.valueOf(redisList.size()).longValue();
            this.currentIndex = -1;
        }

        @Override
        public boolean hasNext() {
            return ++currentIndex < total;
        }

        @Override
        public T next() {
            if (total == 0) {
                throw new IndexOutOfBoundsException("max " + total + ",index:" + currentIndex);
            } else if (currentIndex < end) {
                return dataCache.get(currentIndex % dataCache.size());
            } else {
                if (total < redisList.batchNumber) {
                    end = total;
                } else {
                    if (end < total) {
                        start = end;
                        end += redisList.batchNumber;
                        if (end > total) {
                            end = total;
                        }
                    }
                }
                dataCache = redisList.listOperations.range(redisList.cacheKey, start, end - 1);
                return dataCache.get(currentIndex % dataCache.size());
            }
        }
    }

    @Override
    public Iterator<T> iterator() {
        return new BatchRedisListIterator<>(this);
    }
}
